#include "texture.h"
#include <memory.h>
#include <GL/gl.h>

namespace sleek
{
    namespace texture
    {
        texture::texture(core::math::vector2du size, TextureFormat p) : buffer(0), gl(0), isloaded(false), fmt(p)
        {
            CreateGlTexture();
            make(size,pixel(255,0,0,128));
        }
        texture::~texture()
        {
            if(buffer) delete[] buffer;
            FreeGlTexture();
        }
        /* ***************************************** */
        GLuint texture::glTexture()
        {
            if(!glIsTexture(gl)) GenerateGlTexture();
            return gl;
        }
        void texture::updateGl()
        {
            if(!gl) FreeGlTexture();
            gl = 0;
        }
        core::math::vector2du texture::getOriginalSize()
        {
            return original;
        }
        /* ***************************************** */
        void texture::setPixel(core::math::vector2du pos, pixel color)
        {
            core::u8* pixel = &buffer[ (pos.width+pos.height*original.width)*fmt];
            switch(fmt)
            {
                case TXFMT_LUMINANCE:
                    pixel[0] = (color.getRed()+color.getGreen()+color.getBlue())/3;
                break;
                case TXFMT_LUMINANCE_ALPHA:
                    pixel[0] = (color.getRed()+color.getGreen()+color.getBlue())/3;
                    pixel[1] = color.getAlpha();
                break;
                case TXFMT_RGB:
                    pixel[0] = color.getRed();
                    pixel[1] = color.getGreen();
                    pixel[2] = color.getBlue();
                break;
                case TXFMT_RGBA:
                    pixel[0] = color.getRed();
                    pixel[1] = color.getGreen();
                    pixel[2] = color.getBlue();
                    pixel[3] = color.getAlpha();
                break;
            }
            GenerateGlTexture();
        }
        pixel texture::getPixel(core::math::vector2du pos)
        {
            if(pos >= original) return pixel();
            register unsigned long index = (pos.width+pos.height*original.width)*fmt;
            switch(fmt)
            {
                case TXFMT_LUMINANCE:
                    return pixel(buffer[index],buffer[index],buffer[index],255);
                break;
                case TXFMT_LUMINANCE_ALPHA:
                    return pixel(buffer[index],buffer[index],buffer[index],buffer[index+1]);
                break;
                case TXFMT_RGB:
                    return pixel(buffer[index],buffer[index+1],buffer[index+2],255);
                break;
                case TXFMT_RGBA:
                    return pixel(buffer[index],buffer[index+1],buffer[index+2],buffer[index+3]);
                break;
            }
        }
        TextureFormat texture::getFormat()
        {
            return fmt;
        }
        /* ***************************************** */
        void texture::GenerateGlTexture()
        {
            if(!glIsTexture(gl)) CreateGlTexture();
            switch(fmt)
            {
                case TXFMT_LUMINANCE:
                    glTexImage2D(GL_TEXTURE_2D, 0, fmt, (int)original.width, (int)original.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer);
                break;
                case TXFMT_LUMINANCE_ALPHA:
                    glTexImage2D(GL_TEXTURE_2D, 0, fmt, (int)original.width, (int)original.height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, buffer);
                break;
                case TXFMT_RGB:
                    glTexImage2D(GL_TEXTURE_2D, 0, fmt, (int)original.width, (int)original.height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
                break;
                case TXFMT_RGBA:
                    glTexImage2D(GL_TEXTURE_2D, 0, fmt, (int)original.width, (int)original.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
                break;
            }
        }
        void texture::CreateGlTexture()
        {
            if(glIsTexture(gl)) FreeGlTexture();
            glEnable(GL_TEXTURE_2D);
            glGenTextures (1, & gl);
            glBindTexture (GL_TEXTURE_2D, gl);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glDisable(GL_TEXTURE_2D);
        }
        void texture::FreeGlTexture()
        {
            if(!glIsTexture(gl)) return;
            glDeleteTextures(1,&gl);
            gl = 0;
        }
        /* ***************************************** */
        void texture::make(core::math::vector2du size, pixel color)
        {
            if(buffer) delete[] buffer;
            buffer = new core::u8[size.width*size.height*fmt];
            memset(buffer,0,size.width*size.height*fmt);

            for(register int x=0;x<size.width;x++)
                for(register int y=0;y<size.height;y++)
                    setPixel(core::math::vector2du(x,y),color);

            original = original = size;
            GenerateGlTexture();
        }
        texture* texture::clone()
        {
            texture *tmp = new texture(original, fmt);
            memcpy(tmp->buffer, buffer, original.width*original.height*fmt+1);
            tmp->GenerateGlTexture();
            return tmp;
        }
        texture* texture::monochrome()
        {
            texture *tmp = clone();
            for(register int i = 0; i<original.width*original.height*fmt-fmt; i += fmt)
            {
                if(fmt == TXFMT_RGB || fmt == TXFMT_RGBA)
                {
                    tmp->buffer[i+0] = (buffer[i]+buffer[i+1]+buffer[i+2])/3;
                    tmp->buffer[i+1] = (buffer[i]+buffer[i+1]+buffer[i+2])/3;
                    tmp->buffer[i+2] = (buffer[i]+buffer[i+1]+buffer[i+2])/3;
                }
                else break;
            }
            tmp->GenerateGlTexture();
            return tmp;
        }
        void texture::flipVertical()
        {
            for(register int i = 0; i<original.width*original.height*fmt; i+= fmt)
            {
                unsigned char tmp = buffer[i];
                buffer[i] = buffer[i + 2];
                buffer[i + 2] = tmp;
            }

        }
        void texture::flipHorizotal()
        {
            for(register int i = 0; i<original.width*original.height*fmt; i+= fmt)
            {
                unsigned char tmp = buffer[i];
                buffer[i] = buffer[i + 2];
                buffer[i + 2] = tmp;
            }
        }
        void texture::invertColor()
        {
            for(register int i = 0; i<original.width*original.height*fmt; i+= fmt)
            {
                if(fmt == TXFMT_RGB || fmt == TXFMT_RGBA)
                {
                    buffer[i+0] = 255-buffer[i+0];
                    buffer[i+1] = 255-buffer[i+1];
                    buffer[i+2] = 255-buffer[i+2];
                }
                else break;
            }
            FreeGlTexture();
            GenerateGlTexture();
        }
    }
}
